#pragma once
#include <algorithm>
#include <brpc/channel.h>
#include <brpc/server.h>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <log.h>
#include <memory>
#include <ostream>
#include <resolv.h>
#include <sstream>
#include <vector>
#include<filesystem>

#include "etcd.hpp"
#include "storage.pb.h"
#include "Util.hpp"
#include "etcd_path.h"
#include "filetomd5.h"



namespace lyt{
    // 头像存储目录
    // const std::string avatar_dir = "/home/lyt/OnlineJudge/avatars/";
    const std::string avatar_dir = "/home/ltx/code/online-judge/avatars/";
    class Storage{
    public:
        using ptr = std::shared_ptr<Storage>;
        Storage(){}
        ~Storage(){}
        // 1. 生成uuid（之后返回uuid存储到mysql中）
        // 2. 创建文件
        // 3. 将数据写入文件中
        // 4. 返回uuid
        std::string save_file(const std::string& data){
            std::string data_md5 = FileToMD5::md5_string(data);
            std::string md5_file_name = avatar_dir +data_md5;
            if(filesystem::exists(md5_file_name)){
                return data_md5;
            }
            std::ofstream ofs(md5_file_name,std::ios::binary);
            if(!ofs.is_open()) return "";
            ofs.write(data.data(), data.size());
            if(!ofs.good()) return "";
            ofs.close();
            return data_md5;
        }
        // 1.根据uuid打开对应文件
        // 2.读取文件内容返回
        std::string read_file(const std::string& uuid){
            std::string file_path = avatar_dir + uuid;
            std::ifstream ifs(file_path,std::ios::binary);
            
            if(!ifs.is_open()){
                return "";
            }
            
            std::ostringstream oss;
            oss << ifs.rdbuf();
            ifs.close();
            return oss.str();
        }
        // 根据uuid删除对应文件
        void delete_file(const std::string& uuid){
            std::string file_path = avatar_dir + uuid;
            std::ifstream ifs(file_path,std::ios::binary);
            if(!ifs.is_open()){
                return;
            }
            ifs.close();
            std::remove(file_path.c_str());
        }
    };



    class FileStorageServiceImpl: public file::FileStorageService{
    private:
        Storage::ptr _storage;
    public:
        using ptr = std::shared_ptr<FileStorageServiceImpl>;
        FileStorageServiceImpl():_storage(std::make_shared<Storage>()){}
        void SaveFile(::google::protobuf::RpcController* controller,
            const ::file::SaveFileRequest* request,
            ::file::SaveFileResponse* response,
            ::google::protobuf::Closure* done) override{
            brpc::ClosureGuard done_guard(done);
            // 
            std::string uuid = _storage->save_file(request->file_data());
        
            if(uuid.empty()){
            response->set_success(false);
            response->set_message("Failed to save file");
            }else{
            response->set_success(true);
            response->set_message("File saved successfully");
            response->set_file_uuid(uuid);
            }
        }
        void ReadFile(::google::protobuf::RpcController* controller,
                    const ::file::GetFileRequest* request,
                    ::file::GetFileResponse* response,
                    ::google::protobuf::Closure* done) override{
            brpc::ClosureGuard done_guard(done);
        
            std::string data = _storage->read_file(request->file_uuid());
            if(data.empty()){
                debuglog << "data empty!";
                response->set_success(false);
                response->set_message("找不到对应文件");
                return;
            }
            response->set_success(true);
            response->set_message("读取文件成功");
            response->set_file_data(data);
        }
        void DelFile(::google::protobuf::RpcController* controller,
            const ::file::DelFileRequest* request,
            ::file::DelFileResponse* response,
            ::google::protobuf::Closure* done) override{
            brpc::ClosureGuard done_guard(done);

            _storage->delete_file(request->file_uuid());
            response->set_success(true);
            response->set_message("文件删除成功");
        }
        void ReadFiles(google::protobuf::RpcController* controller,
            const ::file::GetFilesRequest* request,
            ::file::GetFilesResponse* response,
            ::google::protobuf::Closure* done) override{
                brpc::ClosureGuard done_guard(done);
                debuglog << "get files";
                std::vector<std::string> data;
                for(int i = 0; i < request->file_uuid_size(); i++){
                    debuglog << "read file " << request->file_uuid(i);
                    string filecontent = _storage->read_file(request->file_uuid(i));
                    if (filecontent == "") {
                        debuglog << "data empty!";
                        response->set_success(false);
                        response->set_message("找不到对应文件");
                        return ;
                    }
                    infolog << "insert file data";
                    response->mutable_file_data()->insert({request->file_uuid(i),filecontent});
                }
                response->set_success(true);
                response->set_message("读取文件成功");
                infolog << "return getfiles";
            }
    };

    class FileStorageServerBuild;
    class FileStorageServer{
    public:
        friend class FileStorageServerBuild;
        using ptr = std::shared_ptr<FileStorageServer>;
        FileStorageServer(std::shared_ptr<brpc::Server> server,
                            std::shared_ptr<lyt::Register> regis,
                            FileStorageServiceImpl::ptr file_storage
                        ):_server(server),
                        _register(regis),
                        _file_storage(file_storage)
        {}
        void start(){
            _server->RunUntilAskedToQuit();
        }
    private:
        std::shared_ptr<brpc::Server> _server;
        std::shared_ptr<lyt::Register> _register;
        FileStorageServiceImpl::ptr _file_storage;
    };


    class FileStorageServerBuild{
    public:
        FileStorageServerBuild& make_file_storage_service(){
            _file_stroage = std::make_shared<FileStorageServiceImpl>();
            return *this;
        }
        FileStorageServerBuild& make_brpc_server(const size_t port){
            if(!_file_stroage){
                errorlog << "FileStorageService uninitizalier";
                abort();
            }
            _server = std::make_shared<brpc::Server>();
            if(_server->AddService(&(*_file_stroage),brpc::SERVER_DOESNT_OWN_SERVICE) != 0){
                errorlog << "Failed to add service: FileStorageService";
                abort();
            }
            brpc::ServerOptions options;
            options.idle_timeout_sec = -1;
            if(_server->Start(port,&options) != 0){
                errorlog << "Failed to start service: FileStorageService";
                abort();
            }
            return *this;
        }
        // value服务的地址
        FileStorageServerBuild& make_register(const std::string& host,const std::string& value){
            if(!_server){
                errorlog << "Can't register to etcd,because FileStorageService not start";
                abort();
            }
            _register = std::make_shared<lyt::Register>(host);
            string key = lyt::file_path;
            key += "/127.0.0.1:38082";
            if(!_register->registory(key, value)){
                errorlog << "Fail register FileStorageService to etcd";
                abort();
            }
            return *this;    
        }
        FileStorageServer::ptr build(){
            if(!_server){
                errorlog << "Failed to start service: FileStorageService";
                abort();
            }if(!_register){
                errorlog << "Fail register FileStorageService service to etcd";
                abort();
            }if(!_file_stroage){
                errorlog << "Fail to addservice FileStorage";
                abort();
            }

            auto ptr =  std::make_shared<FileStorageServer>(_server,_register,_file_stroage);
            return ptr;
        }

    private:
        std::shared_ptr<brpc::Server> _server;
        std::shared_ptr<lyt::Register> _register;
        FileStorageServiceImpl::ptr _file_stroage;
    };
}

